/**
 *
 * @Author: Marek Zakowicz <mazak (at) debian (dot) linux.org.pl>
 *
 * Copyright (c) 2005, 2006, 2007 Marek Zakowicz. All rights reserved.
 *
 * Licence: This code may be used, (re)distribute and/or modify under the terms one of the following two kinds of licence:
 *          1) as free software under GPL 3 (General Public Licence ver. 3 as published by the Free Software Foundation)
 *          2) as software under the terms individual licence from Marek Zakowicz
 *
 *          This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *          without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package jsp2php;

import java.util.*;
import java.util.regex.*;
import java.io.*;

import static jsp2php.DeclarationAction.*;

public class JSPFTranslator {

   Pattern questionPattern = Pattern.compile(".*[?].*[:].*");
   Pattern  numericPattern = Pattern.compile("[0123456789.]+(E[-]?[0123456789]*)?");
   NameSpace            ns = new NameSpace();

   JSPFTranslator() {
      try {
         ns.declare("request",  JavaType.value_type("javax.servlet.http.HttpServletRequest")  );
         ns.declare("response", JavaType.value_type("javax.servlet.http.HttpServletResponse") );
      }
      catch(TranslatorException e) {
         System.err.println( "Tranlator exception ocurred: " + e);
      }
   }

   Translation assign(String var_type, String var_id, Translation expr) throws TranslatorException {
      Translation tn = new Translation();

      tn.type        = JavaType.statement();
      tn.text        = " $" + var_id +" = "+ expr.text;
      tn.not_null    = expr.not_null;
      tn.consistent  = false;

      return tn;
   }

   // TODO: zmienic nazwe na declare_var
   Translation declare(String type_id, String id, DeclarationAction da) throws TranslatorException {
      JavaType type = JavaType.value_type(type_id);
      ns.declare(id, type);
      if(da == DA_NONE) {
         return null;
      }

      Translation tn = new Translation();

      if(da == DA_COMMENT_TYPE_ONLY) {
         tn.text = "/* "+ type.translator().php_name() +" */";
         tn.type = JavaType.comment();
         return tn;
      }

      if(da == DA_COMMENT) {
         tn.text = "/* "+ type.translator().php_name() +" "+ id +" */";
         tn.type = JavaType.comment();
         return tn;
      }

      // DA_DEFAULT
      tn.type = JavaType.value_type(type_id);
      if(type_id.equals("boolean")) {
         tn.text="$"+ id +" = false";
      }
      else if(type_id.equals("double")) {
         tn.text="$"+ id +" = 0.0";
      }
      else if(type_id.equals("int")) {
         tn.text="$"+ id +" = 0";
      }
      else if( tn.type == JavaType.string() ) {
         tn.text="$"+ id +" = null";
      }
      else {
         throw new TranslatorException("Unexpected type: "+ type_id +".  Only boolean, double, int, String allowed.");
      }
      return tn;
   }

   public Translation field(String name) throws TranslatorException {
      Translation tn = new Translation();
      tn.type        = ns.type(name);
      if(tn.type == null) {
         throw new TranslatorException("Unknown name "+ name);
      }
      if( tn.type.is_value() ) { // jesli pole rozwiazywane na poziomie glownego translatora jest wartoscia, to jest zmienna
         tn.text = "$"+ name;
      }
      tn.not_null    = false;
      tn.consistent  = true;
      return tn;
   }

   public Translation bit(char operator, Translation left, Translation right) throws TranslatorException {
      if( left == null || left.text == null || left.text.length() == 0 || right == null || right.text == null || right.text.length() == 0 ) {
         throw new TranslatorException("Bitwise operator \""+ operator +"\" expects not empty operands!");
      }
      if( left.type == JavaType.integer() ) {
         return new Translation(left.text +" "+ operator +" "+ right.text, left.type, false, left.not_null && right.not_null);
      }
      return new Translation("(bool) ( "+ left.text +" "+ operator +" "+ right.text +" )", left.type, false, left.not_null && right.not_null);
   }

   public Translation unary_arithmetic(char operator, Translation expr) throws TranslatorException {
      if( expr == null || expr.text == null || expr.text.length() == 0 ) {
         throw new TranslatorException("Unary operator \""+ operator +"\" expects not empty operand!");
      }
      if( expr.consistent && numericPattern.matcher(expr.text).matches() ) {
         // literal
         expr.text = operator + expr.text;
      }
      else {
         // not literal
         expr.text = operator + " " + expr.text;
      }
      expr.consistent = false;
      return expr;
   }

   public Translation question(Translation first, Translation second, Translation third) throws TranslatorException {
      if( first  == null || first.text  == null || first.text.length()  == 0 ||
          second == null || second.text == null || second.text.length() == 0 ||
          third  == null || third.text  == null || third.text.length()  == 0 ) {
         throw new TranslatorException("Ternary operator \"?:\" expects not empty operands!");
      }
      String f = questionPattern.matcher(first.text ).matches() ? first.paren()  : first.text;
      String s = questionPattern.matcher(second.text).matches() ? second.paren() : second.text;
      String t = questionPattern.matcher(third.text ).matches() ? third.paren()  : third.text;
      return new Translation(f +" ? "+ s +" : "+ t, second.type, false, second.not_null && third.not_null);
   }

   public Translation add(Translation left, Translation right) throws TranslatorException {
      IValueTranslator tr = (IValueTranslator) TypeCalculator.add_type(left.type, right.type).translator();
      return tr.add(left, right);
   }

   public Translation add_eq(Translation left, Translation right) throws TranslatorException {
      IValueTranslator tr = (IValueTranslator) TypeCalculator.add_type(left.type, right.type).translator();
      return tr.add_eq(left, right);
   }

   public Translation div(Translation left, Translation right) throws TranslatorException {
      IValueTranslator tr = (IValueTranslator) TypeCalculator.mul_type(left.type, right.type).translator();
      return tr.div(left, right);
   }

   public Translation div_eq(Translation left, Translation right) throws TranslatorException {
      IValueTranslator tr = (IValueTranslator) TypeCalculator.mul_type(left.type, right.type).translator();
      return tr.div_eq(left, right);
   }

   public Translation op_eq(String operator, Translation left, Translation right) throws TranslatorException {
      // Rzutowania nie sa konieczne, poniewaz dla typow JSPF, java pozwala tylko na niejawne rzutowanie rozszerzajace int -> double
      String left_text = left.text;
      if( right.modifiedVariables.contains(left_text) ) {
         return new Translation(left_text + " = " + left_text +" "+ operator +" "+ right.text, left.type, false, left.not_null && right.not_null, right.modifiedVariables);
      }

      Set<String> modifiedVariables = new HashSet<String>( right.modifiedVariables );
      modifiedVariables.add(left_text);
      return new Translation(left_text + " "+ operator +"= " + right.text, left.type, false, left.not_null && right.not_null, modifiedVariables);
   }

}
